import { Engine, Scene, ArcRotateCamera, Vector3, HemisphericLight, Mesh, MeshBuilder, StandardMaterial, Color3 } from 'babylonjs';
import { Client, Room } from "colyseus.js";

export class GameScene {
  private engine: Engine;
  private scene: Scene;
  private camera: ArcRotateCamera;
  private player!: Mesh;
  private room!: Room;
  private players: Map<string, Mesh> = new Map();

  constructor(private canvas: HTMLCanvasElement) {
    this.engine = new Engine(this.canvas, true);
    this.scene = new Scene(this.engine);

    this.camera = new ArcRotateCamera("camera1", Math.PI / 2, Math.PI / 4, 10, Vector3.Zero(), this.scene);
    this.camera.detachControl()

    new HemisphericLight("light1", new Vector3(1, 1, 0), this.scene);

    this.connectToServer()
    this.setupInput();

    this.engine.runRenderLoop(() => {
      this.scene.render();
    });
  }

  private async connectToServer() {
    const client = new Client("https://mmo-service.onrender.com");
    this.room = await client.joinOrCreate("game",{
      color:new Color3(Math.random(),Math.random(),Math.random()).toHexString(),
      x:Math.random(),
      y:Math.random(),
      z:Math.random()
    });
    // console.log('join or create', this.room.roomId, this.room.sessionId)

    this.room.onMessage('player-joined', (message) => {
      // console.log('client joined', message.sessionId)
      const { sessionId, player } = message;
      this.addPlayer(sessionId, player);
    });

    this.room.onMessage('player-left', (message) => {
      // console.log('client left', message.sessionId)
      const { sessionId } = message;
      this.removePlayer(sessionId);
    });

    this.room.onMessage("player-moved", (message) => {
      // console.log('client player moved', message)
      const { sessionId, x, y, z } = message;
      this.updatePlayerPosition(sessionId, x, y, z);
    });

    this.room.onMessage("room-state", (message) => {
      // console.log("收到房间状态:", message);
      message.players.forEach(([sessionId, playerData]: [string, any]) => {
        if (sessionId !== this.room.sessionId) {
          if(!this.players.get(sessionId))
          this.addPlayer(sessionId, playerData);
        }
      });
    });

    this.scene.onBeforeRenderObservable.add(() => {
      const delta = this.engine.getDeltaTime() / 1000;
      const speed = 5;
      let moved = false;
      if (this.inputMap["ArrowUp"]) {
        this.player.position.z += speed * delta;
        moved = true;
      }
      if (this.inputMap["ArrowDown"]) {
        this.player.position.z -= speed * delta;
        moved = true;
      }
      if (this.inputMap["ArrowLeft"]) {
        this.player.position.x += speed * delta;
        moved = true;
      }
      if (this.inputMap["ArrowRight"]) {
        this.player.position.x -= speed * delta;
        moved = true;
      }
      if (moved) {
        this.room.send("move", {
          x: this.player.position.x,
          y: this.player.position.y,
          z: this.player.position.z
        });
      }
    });
  }

  private addPlayer(sessionId: string, playerData: any): void {
    console.log('client addPlayer', sessionId, playerData)
    const player= this.generatePlayerMesh(sessionId,playerData);
    this.players.set(sessionId, player);

    if(sessionId===this.room.sessionId){
      this.player=player;
    }
  }

  private removePlayer(sessionId: string) {
    const player = this.players.get(sessionId);
    if (player) {
      player.dispose();
      this.players.delete(sessionId);
    }
  }

  private inputMap: { [key: string]: boolean } = {};

  private setupInput() {
    this.scene.onKeyboardObservable.add((kbInfo) => {
      if (kbInfo.type === 1) {
        this.inputMap[kbInfo.event.key] = true;
      } else if (kbInfo.type === 2) {
        this.inputMap[kbInfo.event.key] = false;
      }
    });
  }

  private updatePlayerPosition(sessionId: string, x: number, y: number, z: number): void {
    const player = this.players.get(sessionId);
    // console.log('updateOtherPlayerPosition', sessionId, otherPlayer, x,y,z)
    if (player) {
      player.position.set(x, y, z);
    }
  }

  private generatePlayerMesh(name:string,data:any):Mesh {
    const m= MeshBuilder.CreateBox("player"+name, { size: 1 }, this.scene);
    m.position.set(data.x,data.y,data.z);
    const mat=new StandardMaterial("playerMat"+name,this.scene);
    mat.diffuseColor=Color3.FromHexString(data.color);
    m.material=mat;
    return m;
  }

}